/*
 * Xmc.c
 *
 *  Created on: Oct 13, 2018
 *      Author: ax
 */
#include <Hwi.h>
#include <Hw/Xmc.h>

#include <ti/csl/csl_cacheAux.h>


#define L1PCFG   ((RegPtr) 0x01840020)
#define L1PCC    ((RegPtr) 0x01840024)
#define L1DCFG   ((RegPtr) 0x01840040)
#define L1DCC    ((RegPtr) 0x01840044)
#define L2WBAR   ((RegPtr) 0x01844000)
#define L2WWC    ((RegPtr) 0x01844004)
#define L2WIBAR  ((RegPtr) 0x01844010)
#define L2IBAR   ((RegPtr) 0x01844018)
#define L2WB     ((RegPtr) 0x01845000)
#define L2WBINV  ((RegPtr) 0x01845004)
#define L1PINV   ((RegPtr) 0x01845028)
#define L1DWB    ((RegPtr) 0x01845040)
#define L1DWBINV ((RegPtr) 0x01845044)
#define MAR      ((RegPtr) 0x01848000)
#define XPFCMD   ((RegPtr) 0x08000300)

#define MAXWC       0xFF00      /* Max word count per cache operations */


#define Cache_atomicBlockSize 0x400



/*
 *  ======== Cache_all ========
 */
void Cache_all(volatile Uint32   *cacheReg) {
    Uint32   mask;

    /* disable interrupts */
    mask = Hwi_disable();

    /* wait for any previous cache operation to complete */
    while (*L2WWC != 0) {
        /* open a window for interrupts */
        Hwi_restore(mask);

        /* disable interrupts */
        mask = Hwi_disable();
    }

    /* perform global write back of cache */
    *cacheReg = 1;

    /* restore interrupts */
    Hwi_restore(mask);

    /* wait until cache operation completes */
    while (*cacheReg) {
        ;
    }
}

/*
 *  ======== Cache_wbInvAll ========
 *  Performs a global write back and invalidate.  All cache lines are
 *  invalidated in L1P cache.  All cache lines are written back to L2 or
 *  or external then invalidated in L1D cache.  All cache lines are
 *  written back to external and then invalidated in L2 cache.
 */
void Cache_wbInvAll() {
    Cache_all(L2WBINV);
}

/*
 *  ======== Cache_invPrefetchBuffer ========
 *  Invalidate the prefetch buffer.
 */
void Cache_invPrefetchBuffer() {
    volatile Uint32   *xpfcmd;

    xpfcmd = XPFCMD;

    xpfcmd[0] = 1;
}

/*
 *  ======== Cache_block ========
 *  This internal function used by the block cache APIs.
 */
void Cache_block(void* blockPtr, size_t byteCnt, bool wait,
    volatile Uint32   *barReg)
{
    volatile Uint32   *bar;
    volatile Uint32   *wc;
    volatile Uint32   *marBase;
    Uint32   maxAddr, marNum;
    Uint32   firstMar, lastMar;
    int wordCnt, incCnt;
    Uint32   mask;
    Uint32   alignAddr;

    /*
     *  Get the base address and word count register.
     *  wc is one word after bar on c64x+ cache.
     */
    bar = barReg;
    wc = bar + 1;

    /* word align the base address */
    alignAddr = ((Uint32  )blockPtr & ~3);

    /* convert from byte to word since cache operation takes words */
    wordCnt = (byteCnt + 3 + ((Uint32  )blockPtr - alignAddr)) >> 2;

    /* determine the increment count */
    if (Cache_atomicBlockSize) {
        incCnt = Cache_atomicBlockSize;
    }
    else {
        incCnt = MAXWC;
    }

    /* loop until word count is zero or less */
    while (wordCnt > 0) {

        /* critical section -- disable interrupts */
        mask = Hwi_disable();

        /* wait for any previous cache operation to complete */
        while (*L2WWC != 0) {
            /* open a window for interrupts */
            Hwi_restore(mask);

            /* disable interrupts */
            mask = Hwi_disable();
        }

        /* set the word address and number of words to invalidate */
        *bar = alignAddr;
        *wc = (wordCnt > incCnt) ? incCnt : wordCnt;

        /*
         *  Silicon errata sprz331a Advisory 14.
         *  Due to c66 silicon bug [see SDOCM00076053] spin with
         *  interrupts disabled here if atomicBlockSize != 0.
         *  Cache_wait() is doing 2 mfences so no need to spin for 16 NOPs
         */
        if (Cache_atomicBlockSize) {
            Cache_wait();
        }

        /* end of critical section -- restore interrupts */
        Hwi_restore(mask);

        /*
         * reduce word count by the increment count and
         * increase base address by increment count.
         */
        wordCnt -= incCnt;
        alignAddr += (incCnt * sizeof(int));
    }

    /* invalidate prefetch buffer if necessary */
    if ((barReg == L2WIBAR) || (barReg == L2IBAR)) {
        /* set the marBase addr */
        marBase = MAR;

        /* caculate the maximum address */
        maxAddr = (Uint32  )blockPtr + (byteCnt - 1);

        /* range of MAR's that need to be modified */
        firstMar = (Uint32  )blockPtr >> 24;
        lastMar = (Uint32  )maxAddr >> 24;

        /* loop through the number of MAR registers */
        for (marNum = firstMar; marNum <= lastMar; marNum++) {
            /* if prefetch bit enabled, invalidate prefetch buffer */
            if (marBase[marNum] & MAR_FPX) {
                Cache_invPrefetchBuffer();
                break;
            }
        }
    }

    /*
     *  Only wait here if atomicBlockSize is 0 and 'wait' is true.
     *  When atomicBlockSize != 0, the wait already happens above.
     */
    if ((Cache_atomicBlockSize == 0) && wait) {
        Cache_wait();
    }
}

/*
 *  ======== Cache_invL1pAll ========
 *  Performs a global invalidate of L1P cache.
 *  Polls the L1P invalidate register until done.
 */
void Cache_invL1pAll()
{
    Cache_all(L1PINV);
}

/*
 *  ======== Cache_wbAll ========
 *  Perform a global write back.  There is no effect on L1P cache.  All cache
 *  lines are left valid in L1D cache and the data in L1D cache is written
 *  back L2 or external.  All cache lines are left valid in L2 cache and the
 *  data in L2 cache is written back to external.
 */
void Cache_wbAll()
{
    Cache_all(L2WB);
}

/*
 *  ======== Cache_wbL1dAll ========
 *  Performs a global write back of L1D cache
 */
void Cache_wbL1dAll()
{
    Cache_all(L1DWB);
}

/*
 *  ======== Cache_wbInvL1dAll ========
 *  Performs a global write back then invalidate of L1D cache
 */
void Cache_wbInvL1dAll()
{
    Cache_all(L1DWBINV);
}

/*
 *  ======== Cache_inv ========
 *  Invalidate the range of memory within the specified starting address and
 *  byte count.  The range of addresses operated on gets quantized to whole
 *  cache lines in each cache.  All cache lines in range are invalidated in L1P
 *  cache.  All cache lines in range are invalidated in L1D cache.
 *  All cache lines in range are invaliated in L2 cache.
 */
void Cache_inv(void* blockPtr, size_t byteCnt, Cache_Type type, bool wait)
{
    Cache_block(blockPtr, byteCnt, wait, L2IBAR);
}

/*
 *  ======== Cache_wb ========
 *  Writes back the range of memory within the specified starting address
 *  and byte count.  The range of addresses operated on gets quantized to
 *  whole cache lines in each cache.  There is no effect on L1P cache.
 *  All cache lines within the range are left valid in L1D cache and the data
 *  within the range in L1D cache will be written back to L2 or external.
 *  All cache lines within the range are left valid in L2 cache and the data
 *  within the range in L2 cache will be written back to external.
 */
void Cache_wb(void* blockPtr, size_t byteCnt, Cache_Type type, bool wait)
{
    Cache_block(blockPtr, byteCnt, wait, L2WBAR);
}

/*
 *  ======== Cache_wbInv ========
 *  Writes back and invalidates the range of memory within the specified
 *  starting address and byte count.  The range of addresses operated on gets
 *  quantized to whole cache lines in each cache.  All cache lines within range
 *  are invalidated in L1P cache.  All cache lines within the range are
 *  written back to L2 or external and then invalidated in L1D cache
 *  All cache lines within the range are written back to external and then
 *  invalidated in L2 cache.
 */
void Cache_wbInv(void* blockPtr, size_t byteCnt, Cache_Type type, bool wait)
{
    Cache_block(blockPtr, byteCnt, wait, L2WIBAR);
}


/*
 *  ======== Cache_setMar ========
 *  Set the MAR register(s) that corresponds to the specified address range.
 */
void Xmc_setMar(Uint32   base4k, Uint32   end4k, Uint32   value) {
    uint16_t marNum, firstMar, lastMar;
    volatile Uint32  * const marBase = MAR;

    /* range of MAR's that need to be modified */
    firstMar = base4k >> (24/*16M*/ - 12/*4k*/);
    lastMar = (end4k - 1) >> (24 - 12) ;

    /* write back invalidate all cached entries */
    Cache_wbInvAll();

    /* loop through the number of MAR registers affecting the address range */
    for (marNum = firstMar; marNum <= lastMar; marNum++) {
        /* set the MAR registers to the specified value */
        marBase[marNum] = value;
    }
}


#include <c6x.h>
#include <ti/csl/csl_msmcAux.h>

void
Xmc_map(
    Uint8  priorityIndex,
    Uint32 pa4K,
    Uint32 va4k,
    Uint8  pow2Size,
    Uint8  premission
){
    Uint32 H = CSL_FMK(XMC_XMPAXH_BADDR, va4k)
             | CSL_FMK(XMC_XMPAXH_SEGSZ, pow2Size);
    Uint32 L = CSL_FMK(XMC_XMPAXL_RADDR, pa4K)
             | CSL_FMK(XMC_XMPAXL_PERM, premission);

    hXmc->XMPAX[priorityIndex].XMPAXH = H;
    hXmc->XMPAX[priorityIndex].XMPAXL = L;

    //MAP MSMC, this is only used for MSMC remap right now.
    if (premission & MPAX_EXT) {
        Uint16 master = DNUM;
        priorityIndex = 8 - (16 - priorityIndex);

        if (TRUE == CSL_MSMC_isSESLocked(master))
            CSL_MSMC_unlockSES(master);

        hMsmc->SES_MPAX_PER_PRIVID[master].SES[priorityIndex].MPAXH = H;
        hMsmc->SES_MPAX_PER_PRIVID[master].SES[priorityIndex].MPAXL = L;
        CSL_MSMC_lockSES(DNUM);

        if (master == 0) {//map other peripheral masters.
            for (master = 8; master < 16; master++) {
                if (TRUE == CSL_MSMC_isSESLocked(master))
                    CSL_MSMC_unlockSES(master);

                hMsmc->SES_MPAX_PER_PRIVID[master].SES[priorityIndex].MPAXH = H;
                hMsmc->SES_MPAX_PER_PRIVID[master].SES[priorityIndex].MPAXL = L;

                CSL_MSMC_lockSES(master);
            }
        }
    }
}
